# -*- coding: utf-8 -*-
import random
from datetime import datetime
from decimal import Decimal

from sqlalchemy import func

from common.bankcard.db import get_withdraw_bankcard_by_name
from common.bankcard.model import BankcardData
from common.risk.model import RiskyUser, RiskyIP, ACTION_TYPE, RISK_STATUS
from common.utils import exceptions as err
from common.utils import track_logging
from common.utils import tz
from common.utils.db import list_object
from common.utils.db import paginate
from common.utils.decorator import sql_wrapper
from common.withdraw.model import *

_LOGGER = track_logging.getLogger(__name__)


@sql_wrapper
def create_order(params):
    user = RiskyUser.query.filter(
        RiskyUser.mch_id == params['mch_id']).filter(
        RiskyUser.user_id == params['user_id']).first()
    if user and user.withdraw_status == RISK_STATUS.DISABLED:
        raise err.ParamError(u'用户禁止下分操作')
    try:
        withdraw_order = WithdrawOrder()
        for k, v in params.iteritems():
            setattr(withdraw_order, k, v)
        withdraw_order.status = WITHDRAW_ORDER_STATUS.WAIT
        withdraw_order.third_type = WITHDRAW_THIRD_TYPE.MANUAL
        withdraw_order.created_at = datetime.utcnow()
        withdraw_order.save(auto_commit=False)

        # 更新玩家风控数据
        order_data = withdraw_order.as_dict()
        if not user:
            user = RiskyUser()
            for param in ['mch_id', 'user_id', 'star', 'client_ip', 'region', 'register_day']:
                setattr(user, param, order_data.get(param, 0))
            for param in ['recharge_avg', 'recharge_total', \
                          'recharge_req_count', 'recharge_succ_count', 'recharge_fail_count', \
                          'recharge_succ_rate', 'withdraw_total', 'withdraw_succ_count', \
                          'withdraw_fail_count', 'withdraw_req_count']:
                setattr(user, param, 0)
            user.withdraw_status = 1
        else:
            for param in ['star', 'client_ip', 'region']:
                setattr(user, param, order_data.get(param, 0))
        user.withdraw_req_count += 1
        user.save(auto_commit=False)

        user_ip = RiskyIP()
        user_ip.risk_user_id = user.id
        user_ip.action_type = ACTION_TYPE.WITHDRAW
        user_ip.ip = params.get('client_ip', '127.0.0.1')
        user_ip.region = params.get('region')
        user_ip.created_at = datetime.utcnow()
        user_ip.save(auto_commit=False)

        orm.session.commit()
        return withdraw_order
    except Exception as e:
        _LOGGER.exception('create withdraw order error, %s', e)
        orm.session.rollback()
        raise err.DataError()
    finally:
        orm.session.close()


@sql_wrapper
def update_order(order_id, target_status, detail, admin, trans_card_name=None):
    ''' 控制台更新下分订单 '''
    try:
        withdraw_order = WithdrawOrder.query.filter(WithdrawOrder.id == order_id).first()
        if not withdraw_order:
            raise err.ParamError("withdraw order_id not found")

        # 状态机及锁定者判断
        if target_status == WITHDRAW_ORDER_STATUS.LOCKED:
            # if withdraw_order.status not in [WITHDRAW_ORDER_STATUS.WAIT, WITHDRAW_ORDER_STATUS.THIRD_FAIL, WITHDRAW_ORDER_STATUS.THIRD_UNKNOWN]:
            #     raise err.DataError('Not Allowed')
            if withdraw_order.status == WITHDRAW_ORDER_STATUS.WAIT and withdraw_order.admin:
                raise err.ParamError('Order locked by %s' % withdraw_order.admin)
            withdraw_order.admin = admin
            withdraw_order.third_type = WITHDRAW_THIRD_TYPE.MANUAL
        elif target_status in [WITHDRAW_ORDER_STATUS.REJECTED, WITHDRAW_ORDER_STATUS.DONE]:
            if withdraw_order.status not in [WITHDRAW_ORDER_STATUS.LOCKED, WITHDRAW_ORDER_STATUS.THIRD,
                                             WITHDRAW_ORDER_STATUS.THIRD_UNKNOWN]:
                raise err.DataError('Not Allowed')
            if withdraw_order.status == WITHDRAW_ORDER_STATUS.LOCKED and admin != withdraw_order.admin:
                raise err.ParamError('Order locked by %s' % withdraw_order.admin)
        elif target_status == WITHDRAW_ORDER_STATUS.THIRD_UNKNOWN:
            target_status = WITHDRAW_ORDER_STATUS.DONE
            withdraw_order.admin = admin
        else:
            raise err.ParamError("target_status not valid")

        # 更新银行卡和玩家统计数据
        if target_status == WITHDRAW_ORDER_STATUS.DONE:
            if trans_card_name:
                # 下分的时候如果派卡需更新卡的余额
                bankcard = get_withdraw_bankcard_by_name(trans_card_name, withdraw_order.mch_id)
                if bankcard:
                    bankcard.calculate_balance -= Decimal("%.15g" % withdraw_order.amount)
                    bankcard.updated_at = datetime.utcnow()
                    if bankcard.calculate_balance < 0:
                        raise err.ParamError("trans card balance not enough")
                    else:
                        bankcard.save(auto_commit=False)
                else:
                    raise err.ParamError("trans card not valid")

                # 更新卡统计数据
                card_data = BankcardData.query.filter(
                    BankcardData.name == trans_card_name).first()
                card_data.withdraw_count += 1
                card_data.daily_withdraw_count += 1
                card_data.withdraw_total += withdraw_order.amount
                card_data.daily_withdraw_total += withdraw_order.amount
                card_data.save(auto_commit=False)

            # 更新玩家数据
            user = RiskyUser.query.filter(
                RiskyUser.mch_id == withdraw_order.mch_id).filter(
                RiskyUser.user_id == withdraw_order.user_id).first()
            if user:
                user.withdraw_total += withdraw_order.amount
                user.withdraw_succ_count += 1
                user.save(auto_commit=False)

        elif target_status == WITHDRAW_ORDER_STATUS.REJECTED:
            # 更新玩家数据
            user = RiskyUser.query.filter(
                RiskyUser.mch_id == withdraw_order.mch_id).filter(
                RiskyUser.user_id == withdraw_order.user_id).first()
            if user:
                user.withdraw_fail_count += 1
                user.save(auto_commit=False)

        withdraw_order.status = target_status
        withdraw_order.trans_card_name = trans_card_name
        withdraw_order.detail = detail
        withdraw_order.updated_at = datetime.utcnow()
        withdraw_order.third_notify_at = datetime.utcnow()
        withdraw_order.save(auto_commit=False)

        orm.session.commit()
        return withdraw_order
    except Exception, e:
        orm.session.rollback()
        raise e
    finally:
        orm.session.close()


@sql_wrapper
def get_order(order_id):
    return WithdrawOrder.query.filter(WithdrawOrder.id == order_id).first()


@sql_wrapper
def get_order_by_out_trade_no(mch_id, out_trade_no):
    return WithdrawOrder.query.filter(
        WithdrawOrder.mch_id == mch_id).filter(
        WithdrawOrder.out_trade_no == out_trade_no).first()


@sql_wrapper
def list_order(query_dct):
    query, total_count = list_object(query_dct, WithdrawOrder, disable_paginate=True)
    total_amount = query.with_entities(func.sum(WithdrawOrder.amount)).scalar() or 0
    query = paginate(query, query_dct)
    return query.all(), total_count, total_amount


@sql_wrapper
def update_withdraw_notify_status(order_id, notify_status):
    withdraw_order = WithdrawOrder.query.filter(WithdrawOrder.id == order_id).with_lockmode('update').first()
    if withdraw_order.notify_count:
        withdraw_order.notify_count += 1
    else:
        withdraw_order.notify_count = 1
    withdraw_order.notify_at = datetime.utcnow()
    withdraw_order.notify_status = notify_status
    withdraw_order.save()
    return withdraw_order


@sql_wrapper
def get_channel(withdraw_order):
    channels = WithdrawChannel.query.filter(
        WithdrawChannel.mch_id == int(withdraw_order.mch_id)).filter(
        WithdrawChannel.status == WITHDRAW_CHANNEL_STATUS.ENABLED).filter(
        WithdrawChannel.min_amount <= withdraw_order.amount).filter(
        WithdrawChannel.max_amount >= withdraw_order.amount).filter(
        WithdrawChannel.start_hour <= tz.local_now().hour).filter(
        WithdrawChannel.end_hour >= tz.local_now().hour).all()
    if not channels:
        return None
    elif len(channels) == 1:
        return channels[0]
    else:
        return random.choice(channels)


@sql_wrapper
def get_channel_by_withdraw_order(order):
    channel = WithdrawChannel.query.filter(
        WithdrawChannel.id == order.third_type).first()
    return channel


@sql_wrapper
def resend_order(order_id):
    order = WithdrawOrder.query.filter(WithdrawOrder.id == order_id).first()
    if order.status not in [WITHDRAW_ORDER_STATUS.WAIT, WITHDRAW_ORDER_STATUS.THIRD_FAIL,
                            WITHDRAW_ORDER_STATUS.THIRD_UNKNOWN]:
        raise err.DataError('Not Allowed')
    return order
